package cn.hill4j.tool.core.crypto;

import cn.hill4j.tool.core.crypto.exception.CryptoCheckSignErrorException;

import java.util.Objects;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
/**
  * ClassName: ReqDecEncUtils
  * Description: 数据报文加解密工具类
  * @author hillchen
  * Date: 2023/8/8
  * @version: 1.0
  */
public class ReqDecEncUtils {
    /**
     * @author hillchen
     * Description 对数据报文进行加密
     * 数据加密逻辑：1、通过原始数据中的数据密钥对数据进行加密(通常建议数据密钥是一次性的，且对数加密时是进行对称加密)
     *            2、对数据密钥进行加密(通常建议对数据秘密进行非对称加密)
     *            3、对原始数据进行签名
     *            4、将加密后的密文、加密后的数据密钥、数据签名包装到密文报文中
     * Date: 2023/8/8 22:09
     * @param <T> 加密后报文同一格式
     * @param sourceVo 原始数据报文
     * @param encVoBuilder 加密后报文构造器
     * @param dataKeyEncHandler 数据加密密钥获取器
     * @param dataEncHandler 业务数据加密处理器
     * @param signHandler 数据签名处理器
     * @return T
     **/
    public static <T extends ReqEncVo> T encReq(ReqSourceVo sourceVo,
                                         Supplier<T> encVoBuilder,
                                         Function<String,String> dataKeyEncHandler,
                                         BiFunction<String,String,String> dataEncHandler,
                                         Function<ReqSourceVo,String> signHandler){
        T encVo = encVoBuilder.get();
        encVo.putReqTime(sourceVo.findReqTime());
        encVo.putReqSource(sourceVo.findReqSource());
        encVo.putReqId(sourceVo.findReqId());
        encVo.putEncDataKey(dataKeyEncHandler.apply(sourceVo.findDataKey()));
        encVo.putEncReqData(dataEncHandler.apply(sourceVo.findDataKey(),sourceVo.findReqData()));
        encVo.putReqSign(signHandler.apply(sourceVo));
        return encVo;
    }
    /**
     * @author hillchen
     * Description 对数据报文进行解密
     * 数据加密逻辑：1、对密文报文中的数据密钥进行解密(通常建议对数据秘密进行非对称加密)
     *            2、使用解密后的数据密钥对密文报文中的加密业务数据进行解密(通常建议数据密钥是一次性的，且对数加密时是进行对称加密)
     *            3、对解密后的业务数据进行签名
     *            4、比对对解密后的业务数据进行签名串和密文报文中的签名串是否一致
     *            5、组装解密后的数据报文
     * Date: 2023/8/8 22:14
     * @param <T> 解密后报文同一格式
     * @param encVo 原始密文报文
     * @param sourceVoBuilder 解密报文数据构造器
     * @param dataKeyDecHandler 业务数据加密密钥解析器
     * @param dataDecHandler 数据报文解密处理逻辑
     * @param signHandler 数据签名处理器
     *
     * @return T
     **/
    public static  <T extends ReqSourceVo> T decReq(ReqEncVo encVo,
                                         Supplier<T> sourceVoBuilder,
                                         Function<String,String> dataKeyDecHandler,
                                         BiFunction<String,String,String> dataDecHandler,
                                         Function<ReqSourceVo,String> signHandler){
        T sourceVo = sourceVoBuilder.get();
        sourceVo.putReqTime(encVo.findReqTime());
        sourceVo.putReqSource(encVo.findReqSource());
        sourceVo.putReqId(encVo.findReqId());
        sourceVo.putDataKey(dataKeyDecHandler.apply(encVo.findEncDataKey()));
        sourceVo.putReqData(dataDecHandler.apply(sourceVo.findDataKey(),encVo.findEncReqData()));
        String sign = signHandler.apply(sourceVo);
        if (!Objects.equals(sign,encVo.findReqSign())){
            throw new CryptoCheckSignErrorException("请求参数验签失败");
        }
        return sourceVo;
    }
}
